#include "xmppcore/stanzaerrorparser.h"
#include "xmppcore/presence.h"
#include "xmppcore/message.h"
#include "xmppcore/iq.h"
#include "xmppcore/xmppparsingfactory.h"
#include "xmppcore/stanzaerror.h"

#include <QtCore/QXmlStreamReader>

namespace yoohoo_xmppcore {

const int StanzaErrorParser::XML_AMENDED_PART_LENGTH = QString("<stream:stream xmlns:stream='http://etherx.jabber.org/streams'>").length();

StanzaErrorParser::StanzaErrorParser(XmppParsingFactory* parsingFactory, QXmlStreamReader* reader, QString& xml,
		Stanza* stanza) : _parsingFactory(parsingFactory), _reader(reader), _xml(xml), _stanza(stanza) {
}

StanzaError* StanzaErrorParser::parse() {
	Stanza::StanzaKind stanzaKind = Stanza::StanzaKindIq;
	if (dynamic_cast<Presence*>(_stanza)) {
		stanzaKind = Stanza::StanzaKindPresence;
	} else if (dynamic_cast<Message*>(_stanza)) {
		stanzaKind = Stanza::StanzaKindMessage;
	}

	QString senderXml;
	int start = 0;
	while (true) {
		start = _reader->characterOffset();
		QXmlStreamReader::TokenType type = _reader->readNext();
		if (type == QXmlStreamReader::StartElement)  {
			if (_reader->prefix().isEmpty() && _reader->namespaceUri().isEmpty() &&
					isStanzaKindMatched(stanzaKind, _reader->name().toString())) {
				senderXml = readSenderXml(start, stanzaKind);
			}

			break;
		} else {
			return 0;
		}
	}

	QString errorXml;
	if (!senderXml.isNull()) {
		start = _reader->characterOffset();
	}

	int end = 0;
	while (true) {
		end = _reader->characterOffset();
		QXmlStreamReader::TokenType type = _reader->readNext();
		if (type == QXmlStreamReader::EndElement) {
			if (_reader->prefix().toString().isEmpty() && _reader->namespaceUri().toString().isEmpty() &&
					isStanzaKindMatched(stanzaKind, _reader->name().toString())) {
				errorXml = _xml.mid(start - XML_AMENDED_PART_LENGTH, end - start);
				break;
			}
		} else {
			continue;
		}
	}

	XmppElement* obj = _parsingFactory->parse(errorXml);
	if (!obj) {
		return 0;
	}

	StanzaError* error = dynamic_cast<StanzaError*>(obj);
	if (error) {
		error->setStanzaKind(stanzaKind);

		error->setFrom(_stanza->from());
		error->setTo(_stanza->to());
		error->setId(_stanza->id());
		error->setXmlLang(_stanza->xmlLang());
		error->setSenderXml(senderXml);

		return error;
	} else {
		delete obj;
		return 0;
	}
}

QString StanzaErrorParser::readSenderXml(int start, Stanza::StanzaKind stanzaKind) {
	QString kindString = _reader->name().toString();
	while (true) {
		QXmlStreamReader::TokenType type = _reader->readNext();
		if (type != QXmlStreamReader::EndElement)
			continue;

		if (_reader->prefix().toString().isEmpty() && _reader->namespaceUri().toString().isEmpty() &&
					isStanzaKindMatched(stanzaKind, _reader->name().toString())) {
			int end = _reader->characterOffset();
			return _xml.mid(start - XML_AMENDED_PART_LENGTH, end - start);
		}
	}
}

bool StanzaErrorParser::isStanzaKindMatched(Stanza::StanzaKind stanzaKind, QString stanzaKindString) {
	if ((stanzaKind == Stanza::StanzaKindPresence && "presence" == stanzaKindString) ||
		(stanzaKind == Stanza::StanzaKindMessage && "message" == stanzaKindString) ||
			(stanzaKind == Stanza::StanzaKindIq && "iq" == stanzaKindString)) {
		return true;
	} else {
		return false;
	}
}

}
